#include "Card_Head.h"

int Card_TotalNum;			//< IO卡件总数量

/*卡件链表私有函数数组（严格按照.h文件中卡件类型枚举顺序）*/
Card_List_Func_t Card_List_Func[TYP_MAX_CARD] =
{
	/*存放软点的卡件（此处将所有上位下发的软点都存放在这张模拟卡中，方便进行统一管理（该卡件只有一张）*/
	{CARD_WORK(SAMAIO), NULL, NULL, NULL},				//< 软点卡件
	/*MODBUS系列通讯卡件*/
	{CARD_WORK(MODBUS485_MASTER), CHANGE_CARD_LIST_PAR(MODBUS485_MASTER), CHANGE_CARD_PAR(MODBUS485_MASTER), INIT_CARD_STA(MODBUS485_MASTER)},						//< MODBUS-485通讯协议（做主站）
	{CARD_WORK(MODBUS485_SLAVE), CHANGE_CARD_LIST_PAR(MODBUS485_SLAVE), CHANGE_CARD_PAR(MODBUS485_SLAVE), INIT_CARD_STA(MODBUS485_SLAVE)},							//< MODBUS-485通讯协议（做从站）
	{CARD_WORK(MODBUSTCP_MASTER), CHANGE_CARD_LIST_PAR(MODBUSTCP_MASTER), CHANGE_CARD_PAR(MODBUSTCP_MASTER), INIT_CARD_STA(MODBUSTCP_MASTER)},						//< MODBUS-TCP通讯协议（做主站）
	{CARD_WORK(MODBUSTCP_SLAVE), CHANGE_CARD_LIST_PAR(MODBUSTCP_SLAVE), CHANGE_CARD_PAR(MODBUSTCP_SLAVE), INIT_CARD_STA(MODBUSTCP_SLAVE)},							//< MODBUS-TCP通讯协议（做从站）
	/*IEC104系列通讯卡件*/
	{CARD_WORK(IEC104TCP_SLAVE), NULL, CHANGE_CARD_PAR(IEC104TCP_SLAVE), INIT_CARD_STA(IEC104TCP_SLAVE)},															//< IEC104-TCP通讯协议（做从站）
	{CARD_WORK(IEC104TCP_MASTER), NULL, CHANGE_CARD_PAR(IEC104TCP_MASTER), INIT_CARD_STA(IEC104TCP_MASTER)},														//< IEC104-TCP通讯协议（做主站）																																														
    /*MOXA系列通讯卡件*/


	/*其他卡件*/
	{CARD_WORK(LOAD_FORECAST), NULL, CHANGE_CARD_PAR(LOAD_FORECAST), INIT_CARD_STA(LOAD_FORECAST)},														//< 负荷预测卡件（ftp通讯协议）																																													

};

/*存放卡件链表的结构体数组实例化（顺序与.h文件中的宏定义顺序必须一致）*/
List_Tab_t Card_List[TYP_MAX_CARD] =
{
	/*存放软点的卡件（此处将所有上位下发的软点都存放在这张模拟卡中，方便进行统一管理（该卡件只有一张）*/
	{ELE_PNT(SAMAIO), TYPE(SAMAIO), ELE_LEN(SAMAIO), 0, NULL, CLIST_FUNC(SAMAIO)},				//< 软点卡件
	/*MODBUS系列通讯卡件*/
	{ELE_PNT(MODBUS485_MASTER), TYPE(MODBUS485_MASTER), ELE_LEN(MODBUS485_MASTER), 0, CLIST_PAR(MODBUS485_MASTER), CLIST_FUNC(MODBUS485_MASTER)},	//< MODBUS-485通讯协议（做主站）
	{ELE_PNT(MODBUS485_SLAVE), TYPE(MODBUS485_SLAVE), ELE_LEN(MODBUS485_SLAVE), 0, CLIST_PAR(MODBUS485_SLAVE), CLIST_FUNC(MODBUS485_SLAVE)},		//< MODBUS-485通讯协议（做从站）
	{ELE_PNT(MODBUSTCP_MASTER), TYPE(MODBUSTCP_MASTER), ELE_LEN(MODBUSTCP_MASTER), 0, NULL, CLIST_FUNC(MODBUSTCP_MASTER)},							//< MODBUS-TCP通讯协议（做主站）
	{ELE_PNT(MODBUSTCP_SLAVE), TYPE(MODBUSTCP_SLAVE), ELE_LEN(MODBUSTCP_SLAVE), 0, CLIST_PAR(MODBUSTCP_SLAVE), CLIST_FUNC(MODBUSTCP_SLAVE)},		//< MODBUS-TCP通讯协议（做从站）
	/*IEC104系列通讯卡件*/
	{ELE_PNT(IEC104TCP_SLAVE),TYPE(IEC104TCP_SLAVE),ELE_LEN(IEC104TCP_SLAVE),0, NULL, CLIST_FUNC(IEC104TCP_SLAVE)},									//< IEC104-TCP通讯协议 （做从站）
	{ELE_PNT(IEC104TCP_MASTER),TYPE(IEC104TCP_MASTER),ELE_LEN(IEC104TCP_MASTER),0, NULL, CLIST_FUNC(IEC104TCP_MASTER)},								//< IEC104-TCP通讯协议 （做主站）

    /*MOXA系列通讯卡件*/


	/*其他卡件*/
	{ELE_PNT(LOAD_FORECAST),TYPE(LOAD_FORECAST),ELE_LEN(LOAD_FORECAST),0, NULL, CLIST_FUNC(LOAD_FORECAST)},								//< 负荷预测卡件（ftp通讯协议）

};

//************************************
// Method:    	FreeCARDIO					CARD IO内存释放函数
// Programmer:  lww
// Time:		2019/2/16
// Returns:   	void
// Parameter: 	Card_Base_t * base			卡件参变量结构体首地址
//************************************
void FreeCARDIO(Card_Base_t* base)
{
	FREE_Pt(base->AIChannel);
	FREE_Pt(base->AOChannel);
	FREE_Pt(base->DIChannel);
	FREE_Pt(base->DOChannel);
}

//************************************
// Method:    	AddCard						增加卡件函数
// Programmer:  lww
// Time:		2019/2/16
// Returns:   	List_Info_e					LIST_MEMFAIL——内存开辟失败
//											LIST_IDEXIT——所需开辟的ID号的卡件已存在
//											LIST_OK——卡件增加成功				
// Parameter: 	List_Tab_t * arg			链表结构体的指针
// Parameter: 	int ID						新增卡件的ID号
// Parameter: 	IO_Num_t * IO_PinNum		新增卡件的IO引脚数量结构体
//************************************
List_Info_e AddCard(List_Tab_t* arg, unsigned int ID, IO_Num_t* IO_PinNum)
{
	List_Info_e info;
	info = AddElement(arg, ID);					//< 增加元素
	if (info == LIST_IDEXIT)					//< ID号已经存在
	{
		printf("ID = %d has been exist\r\n", ID);
		return LIST_IDEXIT;
	}
	else if (info == LIST_MEMFAIL)				//< 操作内存失败
	{
		printf("Calloc memrory failed in ID = %d\r\n", ID);
		return LIST_MEMFAIL;
	}

	Card_Base_t* base = (Card_Base_t*)SearchId((List_Base_t*)(arg->ppData[0]), ID);
	if (base != NULL)
	{
		if (IO_PinNum != NULL)
		{
			//< 初始化卡件输入输出通道数量
			base->IO_Num = IO_PinNum[0];
			//< 动态开辟卡件输入输出通道属性结构体
			CARD_IO_MEMFAIL_DEAL(base->AIChannel = (AVChannel_t*)calloc(IO_PinNum->AI_Num, sizeof(AVChannel_t)));
			CARD_IO_MEMFAIL_DEAL(base->AOChannel = (AVChannel_t*)calloc(IO_PinNum->AO_Num, sizeof(AVChannel_t)));
			CARD_IO_MEMFAIL_DEAL(base->DIChannel = (DVChannel_t*)calloc(IO_PinNum->DI_Num, sizeof(DVChannel_t)));
			CARD_IO_MEMFAIL_DEAL(base->DOChannel = (DVChannel_t*)calloc(IO_PinNum->DO_Num, sizeof(DVChannel_t)));
		}
		return LIST_OK;
	}
	return LIST_IDNON;
}

//************************************
// Method:    	DelCard						删除卡件函数
// Programmer:  lww
// Time:		2019/2/16
// Returns:   	List_Info_e					LIST_IDNON——所需删除的ID号的卡件不存在
//											LIST_OK——卡件删除成功
// Parameter: 	List_Tab_t * arg			链表结构体的指针
// Parameter: 	int ID						被删除卡件的ID号
//************************************
List_Info_e DelCard(List_Tab_t* arg, unsigned int ID)
{
	Card_Base_t* base = (Card_Base_t*)SearchId((List_Base_t*)(arg->ppData[0]), ID);

	if (base != NULL)
	{
		FreeCARDIO(base);
		if (LIST_OK == DelElement(arg, ID))
			return LIST_OK;
	}
	return LIST_IDNON;
}

//************************************
// Method:    	Channel_Find				卡件通道检索
// Programmer:  lww
// Time:		2019/2/23
// Returns:   	Channel_type_e				检索结果
// Parameter: 	List_Tab_t * arg			链表信息结构体指针
// Parameter: 	unsigned int CardID			卡件ID号
// Parameter: 	unsigned int ChannelID		通道ID号
// Parameter: 	void **Channel_pt			通道信息结构体指针的指针
//************************************
Channel_type_e Channel_Find(unsigned int ChannelID, void** Channel_pt)
{
	int i;
	unsigned short CardType;
	unsigned int CardID;

	if (((ChannelID & 0x80000000) >> 31) == 1)		//< 为软点
	{
		CardType = TYP_SAMAIO;
		CardID = 0;
	}
	else
	{
		CardType = (unsigned short)((ChannelID & 0x1F800000) >> 23);
		CardID = ((ChannelID & 0xFFC0) >> 6) + 1;		//< lijun20220713更改：因大有卡件数大幅上升，此处将读的位数扩充到10位
	}
	
	Card_Base_t* base = (Card_Base_t*)SearchId((List_Base_t*)(Card_List[CardType].ppData[0]), CardID);
	if (base != NULL)
	{
		for (i = 0; i < base->IO_Num.AI_Num; i++)
		{
			if (base->AIChannel[i].Base_Property.ID == ChannelID)
			{
				Channel_pt[0] = (void*)&(base->AIChannel[i]);
				return AI_CHA;
			}
		}
		for (i = 0; i < base->IO_Num.AO_Num; i++)
		{
			if (base->AOChannel[i].Base_Property.ID == ChannelID)
			{
				Channel_pt[0] = (void*)&(base->AOChannel[i]);
				return AO_CHA;
			}
		}
		for (i = 0; i < base->IO_Num.DI_Num; i++)
		{
			if (base->DIChannel[i].Base_Property.ID == ChannelID)
			{
				Channel_pt[0] = (void*)&(base->DIChannel[i]);
				return DI_CHA;
			}
		}
		for (i = 0; i < base->IO_Num.DO_Num; i++)
		{
			if (base->DOChannel[i].Base_Property.ID == ChannelID)
			{
				Channel_pt[0] = (void*)&(base->DOChannel[i]);
				return DO_CHA;
			}
		}
	}
	printf("Can't find No.%d Card's No.%d channel!\r\n", CardID, ChannelID);
	return NODEFINE;
}

//************************************
// Method:    	BcTimeFlg_Set					设置是否广播时间状态标志
// Programmer:  lww
// Time:		2019/4/27
// Returns:   	void
// Parameter: 	DVAV_BASE_Property_t * base		通道基本变量结构体指针
// Parameter: 	bool value						是否广播
//************************************
void BcTimeFlg_Set(DVAV_BASE_Property_t* base, BcIO_Mode_e BcIO_Mode)
{
	switch (BcIO_Mode) {
	case NOT_BCIO:
		base->Is_BcTimeFlg = false;
		base->Is_BcFlg = false;
		break;
	case BCIO:
		base->Is_BcFlg = true;					//< 只对广播IO状态进行设置，不关心是否广播时间，广播时间由其他地方进行设置
		break;
	case BCIO_AND_TIME:
		base->Is_BcTimeFlg = true;
		base->Is_BcFlg = true;
		base->AlarmTime = RealTime_Get();
		break;
	}
}

//************************************
// Method:    	DV_Alarm					数字量通道报警检测
// Programmer:  lww
// Time:		2019/2/22
// Returns:   	void
// Parameter: 	DVChannel_t * base			数字量通道属性结构体指针
//************************************
void DV_Alarm(DVChannel_t* base)
{
	unsigned short* Status;
	bool* CurOut_Value;
	bool* AlarmDef;
	bool* Alarm_Ignore;
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<   赋值操作   >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	Status = &(base->Base_Property.Status);
	CurOut_Value = &(base->Sta_Property.CurOut_Value);
	AlarmDef = &(base->Pra_Property.AlarmDef);
	Alarm_Ignore = &(base->Pra_Property.Alarm_Ignore);
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<   报警操作   >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	/*
	状态字为unsigned short类型，共由1--16共16位构成：
		第3位——报警确认位，1-确认、0-未确认；
		第6位——报警状态位，1-报警、0-非报警；
		第7位——保持状态位，1-保持、0-非保持；
		第8位——强制状态位，1-强制、0-非强制；
		第9位——IO超时，1-超时、0-未超时
	*/
	if (Alarm_Ignore[0] != true)
	{
		if ((CurOut_Value[0] == AlarmDef[0]) && ((Status[0] & 0x20) == 0))			//< 达到报警定义值且状态字中报警状态为未报警，则切换状态为报警状态
		{
			Status[0] = ((Status[0] | 0x20) & 0xFFE0);								//< 前面“或”运算置报警状态为1，“与”运算置第1-5标志位为0
		}
		else if ((CurOut_Value[0] != AlarmDef[0]) && ((Status[0] & 0x20) != 0))		//< 未达到报警定义值但是状态字中报警状态为报警，则切换状态为未报警状态
		{
			Status[0] = (Status[0] & 0xFFC0);										//< 置报警状态为0的同时，置报警确认标志为0，报警确认在报警状态下通过命令字ACK设置
		}
	}
	else
	{
		Status[0] = (Status[0] & 0xFFC0);
	}

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
}

//************************************
// Method:    	DV_Init						数字量通道初始化程序
// Programmer:  lww
// Time:		2019/2/22
// Returns:   	void
// Parameter: 	DVChannel_t * base			数字量通道属性结构体指针
//************************************
void DV_Init(DVChannel_t* base)
{
	struct timespec ts;

	ts = RealTime_Get();
	base->Base_Property.LastReportTime = ts;						//< 初始化上一次广播的时间

	//< 赋初始值
	base->Sta_Property.CurOut_Value = base->Pra_Property.InitValue;
	base->Sta_Property.LastOut_Value = base->Pra_Property.InitValue;

	//< 初始化测点状态（初始化为超时状态）
	base->Base_Property.Status = 0x0100;
	base->Base_Property.PreStatus = 0x0100;

	BcTimeFlg_Set(&base->Base_Property, BCIO_AND_TIME);
}

//************************************
// Method:    	DV_Write					数字量通道写入
// Programmer:  lww
// Time:		2019/2/20
// Returns:   	void
// Parameter: 	DVChannel_t * base			数字量通道属性结构体指针
// Parameter: 	bool value					采样值
// Parameter: 	Channel_OperaMode_e mode	操作模式
//************************************
void DV_Write(DVChannel_t* base, bool value, Channel_OperaMode_e mode)
{
	unsigned short* Status;
	unsigned short* PreStatus;
	bool* CurOut_Value;
	bool* LastOut_Value;
	bool* IEC104_VaryFlg;
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<   赋值操作   >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	Status = &(base->Base_Property.Status);
	PreStatus = &(base->Base_Property.PreStatus);
	CurOut_Value = &(base->Sta_Property.CurOut_Value);
	LastOut_Value = &(base->Sta_Property.LastOut_Value);
	IEC104_VaryFlg = &(base->Base_Property.IEC104_VaryFlg);
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<  更新输出值  >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	LastOut_Value[0] = CurOut_Value[0];					//< 更新上一时刻最终输出值

	//< 更新当前时刻的最终输出值
	if (mode == WRITE)
	{
		if ((Status[0] & 0xC0) == 0)					//< 当前点为非强制、非保持状态
		{
			CurOut_Value[0] = value;
		}
	}
	else if (mode == TURN_FORCE)
	{
		CurOut_Value[0] = value;
	}

	//< 更新IEC104_VaryFlg标志
	if (CurOut_Value[0] != LastOut_Value[0])
		IEC104_VaryFlg[0] = true;
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<  更新状态字  >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	PreStatus[0] = Status[0];
	/*
	状态字为unsigned short类型，共由1--16共16位构成：
		第3位——报警确认位，1-确认、0-未确认；
		第6位——报警状态位，1-报警、0-非报警；
		第7位——保持状态位，1-保持、0-非保持；
		第8位——强制状态位，1-强制、0-非强制；
		第9位——IO超时，1-超时、0-未超时
	*/
	if (mode == TURN_HOLD)													//< 当前进行操作为：切换状态为保持状态
		Status[0] = (unsigned short)((Status[0] & 0xFF3F) | 0x40);			//< 清强制标志位，置保持标志位
	else if (mode == TURN_FORCE)											//< 当前进行操作为：切换状态为强制状态
		Status[0] = (unsigned short)((Status[0] & 0xFF3F) | 0x80);			//< 清保持标志位，置强制标志位
	else if (mode == TURN_TIMEOUT)											//< 当前进行操作为：切换状态为超时状态
		Status[0] = (unsigned short)(Status[0] | 0x100);					//< 置IO超时标志位为1
	else if (mode == WRITE)													//< 当前进行操作为：写入采样值
		Status[0] = (unsigned short)(Status[0] & 0xEFF);					//< 当执行写入采样值时，说明卡件有新值进入，不存在超时情况，则清IO超时标志位
	else if (mode == TURN_NORMAL)											//< 当前进行操作为：切换状态为非强制和非保持状态
		Status[0] = (unsigned short)(Status[0] & 0xFF3F);					//< 清强制和保持标志位
	else if (mode == ALARM_SURE)											//< 当前进行操作为：进行报警确认操作
		Status[0] = (unsigned short)(Status[0] | 0x04);						//< 置报警确认位为1

	//< 有采样值或强制值写入时，进行报警判断
	if (mode == WRITE || mode == TURN_FORCE)
		DV_Alarm(base);
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*
		判断状态是否发生改变，改变是要置广播标志为1
			在此处进行判断的原因：每一次对数据点进行写入操作，数据点的前一时刻状态都会被刷新；
			当广播周期比较大时，很有可能随着数据点状态被刷新两次，前后状态由先前的不等于状态就变为了等于状态
			此时本该被广播的状态就遗漏了，所以要在此处对标志进行置位操作。
	*/
	if (Status[0] != PreStatus[0])
		BcTimeFlg_Set(&base->Base_Property, BCIO_AND_TIME);
}

//************************************
// Method:    	AV_Alarm					模拟量通道报警检测
// Programmer:  lww
// Time:		2019/2/22
// Returns:   	void
// Parameter: 	AVChannel_t * base			模拟量通道属性结构体指针
//************************************
void AV_Alarm(AVChannel_t* base)
{
	unsigned int* AlarmIgnore;
	bool Phy_Ala_Ignore;					//< 屏蔽量程越限报警
	bool Spd_Ala_Ignore;					//< 屏蔽速率越限报警
	bool Limit3_Ala_Ignore;					//< 屏蔽3限报警
	bool Limit2_Ala_Ignore;					//< 屏蔽2限报警
	bool Limit1_Ala_Ignore;					//< 屏蔽1限报警

	float* Alarm_HLimit1;
	float* Alarm_LLimit1;
	float* Alarm_HLimit2;
	float* Alarm_LLimit2;
	float* Alarm_HLimit3;
	float* Alarm_LLimit3;
	float* AlarmDead;
	float* Alarm_Speed_Limit;
	/*float* Alarm_Speed_Dead;*/				//< duan191210 应浙江电科院要求，添加

	float* Phy_HLimit;
	float* Phy_LLimit;

	float* CurOut_Value;
	float* LastOut_Value;
	struct timespec* CurValue_Time;
	struct timespec* LastValue_Time;
	unsigned short* Status;
	unsigned short AlarmStatus = 0;
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<   赋值操作   >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	AlarmIgnore = &(base->Pra_Property.AlarmIgnore);
	Phy_Ala_Ignore = (AlarmIgnore[0] & 0x01) ? true : false;
	Spd_Ala_Ignore = (AlarmIgnore[0] & 0x02) ? true : false;
	Limit3_Ala_Ignore = (AlarmIgnore[0] & 0x04) ? true : false;
	Limit2_Ala_Ignore = (AlarmIgnore[0] & 0x08) ? true : false;
	Limit1_Ala_Ignore = (AlarmIgnore[0] & 0x10) ? true : false;

	Alarm_HLimit1 = &(base->Pra_Property.Alarm_HLimit1);
	Alarm_LLimit1 = &(base->Pra_Property.Alarm_LLimit1);
	Alarm_HLimit2 = &(base->Pra_Property.Alarm_HLimit2);
	Alarm_LLimit2 = &(base->Pra_Property.Alarm_LLimit2);
	Alarm_HLimit3 = &(base->Pra_Property.Alarm_HLimit3);
	Alarm_LLimit3 = &(base->Pra_Property.Alarm_LLimit3);
	AlarmDead = &(base->Pra_Property.AlarmDead);
	Alarm_Speed_Limit = &(base->Pra_Property.Alarm_Speed_Limit);
	/*Alarm_Speed_Dead = &(base->Pra_Property.Alarm_Speed_Dead);	*/		//< duan191210 应浙江电科院要求，添加		
	Phy_HLimit = &(base->Pra_Property.Phy_HLimit);
	Phy_LLimit = &(base->Pra_Property.Phy_LLimit);

	CurOut_Value = &(base->Sta_Property.CurOut_Value);
	LastOut_Value = &(base->Sta_Property.LastOut_Value[0]);
	CurValue_Time = &(base->Sta_Property.CurValue_Time);
	LastValue_Time = &(base->Sta_Property.LastValue_Time);
	Status = &(base->Base_Property.Status);
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<   报警操作   >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	/*
		状态字为unsigned short类型，共由1--16共16位构成：
			第1位——物理量程越限，1-越限、0-未越限
			第2位——速率报警越限，1-越限、0-未越限
			第3、4、5位——报警类型：
									000-无报警
									001-下1限报警
									010-下2限报警
									011-下3限报警
									101-上1限报警
									110-上2限报警
									111-上3限报警
			第6位——报警状态位，1-报警、0-非报警；
			第7位——保持状态位，1-保持、0-非保持；
			第8位——强制状态位，1-强制、0-非强制；
			第9位——IO超时，1-超时、0-未超时
			第12位——报警确认位，1-确认、0-未确认；
	*/
	/* Situation1: 量程限报警未屏蔽 && 当前值大于量程上限或小于量程下限*/
	if ((Phy_Ala_Ignore == false) && (CurOut_Value[0] > Phy_HLimit[0] || CurOut_Value[0] < Phy_LLimit[0]))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第1位物理量程越限位置1且第6位报警状态置1
		AlarmStatus = (unsigned short)((AlarmStatus & 0xE3) | 0x21);
	}
	/* Situation2: 量程限报警未屏蔽 && 当前状态为物理量程越限状态，并且当前值大于量程上限减去死区或小于量程下限加上死区(死区的目的是保证报警状态在刚刚进入上下限范围内时不马上消除报警状态，防止报警状态字反复变化)*/
	else if ((Phy_Ala_Ignore == false) && (((Status[0] & 0x21) == 0x21) && ((CurOut_Value[0] > (Phy_HLimit[0] - AlarmDead[0])) || (CurOut_Value[0] < (Phy_LLimit[0] + AlarmDead[0])))))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第1位物理量程越限位置1且第6位报警状态置1
		AlarmStatus = (unsigned short)((AlarmStatus & 0xE3) | 0x21);
	}
	/* Situation3: 3限报警未屏蔽 && 当前值大于3限报警的上限*/
	else if ((Limit3_Ala_Ignore == false) && (CurOut_Value[0] > Alarm_HLimit3[0]))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x07 << 2));	//< 0x07<<2即置第3、4、5位报警状态为111-上3限报警
	}
	/* Situation4: 3限报警未屏蔽 && 当前报警状态为111-上3限报警，且当前值大于3限报警的上限减去死区*/
	else if ((Limit3_Ala_Ignore == false) && (((Status[0] & 0x3C) == 0x3C) && (CurOut_Value[0] > (Alarm_HLimit3[0] - AlarmDead[0]))))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x07 << 2));	//< 0x07<<2即置第3、4、5位报警状态为111-上3限报警
	}
	/* Situation5: 3限报警未屏蔽 && 当前值小于3限报警的下限*/
	else if ((Limit3_Ala_Ignore == false) && (CurOut_Value[0] < Alarm_LLimit3[0]))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x03 << 2));	//< 0x03<<2即置第3、4、5位报警状态为011-下3限报警
	}
	/* Situation6: 3限报警未屏蔽 && 当前报警状态为011-下3限报警，且当前值小于3限报警的下限加上死区*/
	else if ((Limit3_Ala_Ignore == false) && (((Status[0] & 0x2C) == 0x2C) && (CurOut_Value[0] < (Alarm_LLimit3[0] + AlarmDead[0]))))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x03 << 2));	//< 0x03<<2即置第3、4、5位报警状态为011-下3限报警
	}
	/* Situation7: 2限报警未屏蔽 && 当前值大于2限报警的上限*/
	else if ((Limit2_Ala_Ignore == false) && (CurOut_Value[0] > Alarm_HLimit2[0]))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x06 << 2));	//< 0x06<<2即置第3、4、5位报警状态为110-上2限报警
	}
	/* Situation8: 2限报警未屏蔽 && 当前报警状态为110-上2限报警，且当前值大于2限报警的上限减去死区*/
	else if ((Limit2_Ala_Ignore == false) && (((Status[0] & 0x38) == 0x38) && (CurOut_Value[0] > (Alarm_HLimit2[0] - AlarmDead[0]))))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x06 << 2));	//< 0x06<<2即置第3、4、5位报警状态为110-上2限报警
	}
	/* Situation9: 2限报警未屏蔽 && 当前值小于2限报警的下限*/
	else if ((Limit2_Ala_Ignore == false) && (CurOut_Value[0] < Alarm_LLimit2[0]))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x02 << 2));	//< 0x02<<2即置第3、4、5位报警状态为010-下2限报警
	}
	/* Situation10: 2限报警未屏蔽 && 当前报警状态为010-下2限报警，且当前值小于2限报警的下限加上死区*/
	else if ((Limit2_Ala_Ignore == false) && (((Status[0] & 0x28) == 0x28) && (CurOut_Value[0] < (Alarm_LLimit2[0] + AlarmDead[0]))))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x02 << 2));	//< 0x02<<2即置第3、4、5位报警状态为010-下2限报警
	}
	/* Situation11: 1限报警未屏蔽 && 当前值大于1限报警的上限*/
	else if ((Limit1_Ala_Ignore == false) && (CurOut_Value[0] > Alarm_HLimit1[0]))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x05 << 2));	//< 0x05<<2即置第3、4、5位报警状态为101-上1限报警
	}
	/* Situation12: 1限报警未屏蔽 && 当前报警状态为101-上1限报警，且当前值大于1限报警的上限减去死区*/
	else if ((Limit1_Ala_Ignore == false) && (((Status[0] & 0x34) == 0x34) && (CurOut_Value[0] > (Alarm_HLimit1[0] - AlarmDead[0]))))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x05 << 2));	//< 0x05<<2即置第3、4、5位报警状态为101-上1限报警
	}
	/* Situation13: 1限报警未屏蔽 && 当前值小于1限报警的下限*/
	else if ((Limit1_Ala_Ignore == false) && (CurOut_Value[0] < Alarm_LLimit1[0]))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x01 << 2));	//< 0x01<<2即置第3、4、5位报警状态为001-下1限报警
	}
	/* Situation14: 1限报警未屏蔽 && 当前报警状态为001-下1限报警，且当前值小于1限报警的下限加上死区*/
	else if ((Limit1_Ala_Ignore == false) && (((Status[0] & 0x24) == 0x24) && (CurOut_Value[0] < (Alarm_LLimit1[0] + AlarmDead[0]))))
	{	//< “与”运算保证第3、4、5位报警类型清零，“或”运算保证第6位报警状态置1
		AlarmStatus = (unsigned short)(((AlarmStatus & 0xE3) | 0x20) + (0x01 << 2));	//< 0x01<<2即置第3、4、5位报警状态为001-下1限报警
	}

	/* Situation15: 速率限制报警未屏蔽 && 量程越限未报警 && 速率限制值大于0*/
	if ((Spd_Ala_Ignore == false) && ((AlarmStatus & 0x01) == 0) && (Alarm_Speed_Limit[0] > 0))
	{
		float dx, dv;
		dx = ((float)Dt_Calu(CurValue_Time[0], LastValue_Time[0])) / 1000;		//< 除以1000转化为s
		if (dx > 0)
		{
			dv = fabsf((CurOut_Value[0] - LastOut_Value[0]) / dx);
			/*printf("CurOut_Value[0] = %f\r\n", CurOut_Value[0]);
			printf("LastOut_Value[0] = %f\r\n", LastOut_Value[0]);
			printf("dx = %f\r\n", dx);
			printf("dv = %f \r\n", dv);*/
			
			//< duan191210 下述代码无任何问题，只是未有速率报警死区功能
			if (dv > Alarm_Speed_Limit[0])
				AlarmStatus = (unsigned short)(AlarmStatus | 0x22);	//< 置第2位速率报警位和第6位报警位为1
			else
				AlarmStatus = (unsigned short)(AlarmStatus & 0xFD);		//< duan0607 修改

			
			//< duan191210 添加速率报警死区功能
			//if (dv > Alarm_Speed_Limit[0])
			//	AlarmStatus = (unsigned short)(AlarmStatus | 0x22);//< 置第2位速率报警位和第6位报警位为1
			//else if((AlarmStatus & 0x02) == 0x02 && dv > (Alarm_Speed_Limit[0] - Alarm_Speed_Dead[0]) && dv < Alarm_Speed_Limit[0])		//< duan 101210添加报警死区功能
			//	AlarmStatus = (unsigned short)(AlarmStatus | 0x22);//< 置第2位速率报警位和第6位报警位为1
			//else
			//	AlarmStatus = (unsigned short)(AlarmStatus & 0xFD);
		}
	}

	AlarmStatus = (unsigned short)(AlarmStatus | (Status[0] & 0x08C0));	//< “与”运算获取原状态字中第12位（报警确认）、第7、8位（保持、强制）的信息，“或”运算与新的报警信息进行结合

	//< 如果报警状态发生改变则清空报警确认标志
	if ((AlarmStatus & 0x3F) != (Status[0] & 0x3F))
	{
		AlarmStatus = (AlarmStatus & 0x07FF);
	}

	Status[0] = AlarmStatus;
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
}

//************************************
// Method:    	AV_Init						模拟量通道初始化程序
// Programmer:  lww
// Time:		2019/2/22
// Returns:   	void
// Parameter: 	AVChannel_t * base			模拟量通道属性结构体指针
//************************************
void AV_Init(AVChannel_t* base)
{
	int i;
	struct timespec ts;

	//< 赋初始值
	ts = RealTime_Get();
	base->Sta_Property.CurValue_Time = ts;							//< 初始化前后两次测量值刷新的时间
	base->Sta_Property.LastValue_Time = ts;
	base->Base_Property.LastReportTime = ts;						//< 初始化上一次广播的时间

	base->Sta_Property.CurOut_Value = base->Pra_Property.InitValue;		//< 采用上位设定的初始值初始化测量值			
	
	for (i = 0; i < HIS_VALUE_NUM; i++)
	{
		base->Sta_Property.LastOut_Value[i] = base->Sta_Property.CurOut_Value;
		base->Sta_Property.LastSample_Value[i] = base->Sta_Property.CurOut_Value;
	}

	//< 初始化测点状态（初始化为超时状态）
	base->Base_Property.Status = 0x0100;
	base->Base_Property.PreStatus = 0x0100;

	BcTimeFlg_Set(&base->Base_Property, BCIO_AND_TIME);
}

//************************************
// Method:    	AV_Write					模拟量通道写入
// Programmer:  lww
// Time:		2019/2/20
// Returns:   	void
// Parameter: 	AVChannel_t * base			模拟量通道属性结构体指针
// Parameter: 	float value					采样值（强制状态下传入的为强制值）
// Parameter: 	Channel_OperaMode_e mode	操作模式
//************************************
void AV_Write(AVChannel_t* base, float value, Channel_OperaMode_e mode)
{
#define max(a, b)	(((a) > (b)) ? (a) : (b)) 
#define min(a, b)	(((a) > (b)) ? (b) : (a))				
#define sum_calu	{\
	for (i = 0; i < HIS_VALUE_NUM; i++)\
	{\
		sum += LastSample_Value[i];\
	}\
	sum += value;\
}
//< duan20200207修改       //冒泡排序（升序）
#define bubble_sort {\
	int j = 0;\
	float tmp_float = 0.0;\
	for (i = 0; i < HIS_VALUE_NUM; i++)\
	{\
		for (j = 0; j < HIS_VALUE_NUM - i; j++)\
		{\
			if (Sample_Value[j] > Sample_Value[j + 1])\
			{\
				tmp_float = Sample_Value[j + 1];\
				Sample_Value[j + 1] = Sample_Value[j];\
				Sample_Value[j] = tmp_float;\
			}\
		}\
	}\
}
	
	
	int i;
	float Sample_Value[HIS_VALUE_NUM + 1] = { 0.0 };						//< duan20200207
	float* CurOut_Value;
	float* LastOut_Value;
	float* LastSample_Value;
	unsigned short* Status;
	unsigned short* PreStatus;
	Fiter_Mode_e FilterMode; //滤波类型
	float* slope;	//斜率
	float* intercept;	//截距
	float* TinyValue;	//小信号切除值
	struct timespec ts;
	struct timespec* CurValue_Time;
	struct timespec* LastValue_Time;
	bool* IEC104_VaryFlg;

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<   赋值操作   >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	CurOut_Value = &(base->Sta_Property.CurOut_Value);
	LastOut_Value = &(base->Sta_Property.LastOut_Value[0]);
	LastSample_Value = &(base->Sta_Property.LastSample_Value[0]);
	Status = &(base->Base_Property.Status);
	PreStatus = &(base->Base_Property.PreStatus);
	FilterMode = base->Pra_Property.FilterMode;
	slope = &(base->Pra_Property.slope);
	intercept = &(base->Pra_Property.intercept);	
	TinyValue = &(base->Pra_Property.TinyValue);
	CurValue_Time = &(base->Sta_Property.CurValue_Time);
	LastValue_Time = &(base->Sta_Property.LastValue_Time);
	IEC104_VaryFlg = &(base->Base_Property.IEC104_VaryFlg);
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<  更新输出值  >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	//< 更新上几个时刻的最终输出值
	for (i = HIS_VALUE_NUM - 1; i > 0; i--)
	{
		LastOut_Value[i] = LastOut_Value[i - 1];
	}
	LastOut_Value[0] = CurOut_Value[0];


	//< 更新当前时刻的最终输出值
 	if (mode == WRITE)
	{
		//< 小信号切除  （下限置零）
		if (fabsf(value) < (TinyValue[0]))
			value = 0;

		//< y=ax+b 对采样值进行缩放和位移
		value = value * slope[0] + intercept[0];

		if ((Status[0] & 0xC0) == 0)						//< 当前点为非强制、非保持状态
		{
			if (FilterMode == NO_FITER)						//< 不滤波
			{
				CurOut_Value[0] = value;
			}
			else if (FilterMode == AVERAGE_FITER)			//< duan20200207算数平均滤波-适用于慢变信号。当前采样次数为4次。滤波效果主要取决于采样次数N，N越大，滤波效果越好，但系统灵敏度下降。
			{
				float sum = 0;
				sum_calu;
				CurOut_Value[0] = sum / (HIS_VALUE_NUM + 1);
			}
			else if (FilterMode == MIDVALUE_FITER)			//< duan20200207该中值滤波存在问题，这是均值滤波。删除最大值和最小值，剩余数值求和后取平均值
			{
				//float sum = 0;
				//float max_value = LastSample_Value[0];
				//float min_value = LastSample_Value[0];
				//for (i = 0; i < HIS_VALUE_NUM; i++)		//< duan20200207寻找历史采样值中的最大值、最小值
				//{
				//	max_value = max(max_value, LastSample_Value[i]);
				//	min_value = min(min_value, LastSample_Value[i]);
				//}
				//max_value = max(max_value, value);		//< duan20200207加入当前采样值后，计算采样值中最大值、最小值
				//min_value = min(min_value, value);
				//sum_calu;									//< duan20200207计算采样值综合
				//CurOut_Value[0] = (sum - max_value - min_value) / (HIS_VALUE_NUM - 1);				//< 减去最大值最小值再加上当前采样值，所以综合为-1

				//< duan20200207修改
				memcpy(Sample_Value, LastSample_Value, HIS_VALUE_NUM); 
				Sample_Value[HIS_VALUE_NUM] = value; 
				bubble_sort;
				CurOut_Value[0] = Sample_Value[(HIS_VALUE_NUM + 2) / 2];	//< 取出位于中间位置的值							
			}
			else if (FilterMode == WEIGHTED_FITER)
			{
				//CurOut_Value[0] = (460.0f * LastOut_Value[0] - 312.0f * LastOut_Value[1] + 74.0f * LastOut_Value[3] + 4.25f * (value + 3.0f * LastSample_Value[0] + 3.0f * LastSample_Value[1] + LastSample_Value[2])) / 256.0f;
				CurOut_Value[0] = value;
			}
		}
	}
	else if (mode == TURN_FORCE)
	{
		CurOut_Value[0] = value;
	}

	//< 更新采样值（只有在WRITE模式下，才更新最新采样值，否则最新采样值保持不变）
	for (i = HIS_VALUE_NUM - 1; i > 0; i--)
	{
		LastSample_Value[i] = LastSample_Value[i - 1];
	}
	if (mode == WRITE)
		LastSample_Value[0] = value;

	//< 更新IEC104_VaryFlg标志
	if (CurOut_Value[0] - LastOut_Value[0] != 0)
		IEC104_VaryFlg[0] = true;
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<   更新时间   >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	ts = RealTime_Get();
	LastValue_Time[0] = CurValue_Time[0];
	CurValue_Time[0] = ts;
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<  更新状态字  >>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/
	PreStatus[0] = Status[0];
	/*
	状态字为unsigned short类型，共由1--16共16位构成：
		第7位——保持状态位，1-保持、0-非保持；
		第8位——强制状态位，1-强制、0-非强制；
		第9位——IO超时，1-超时、0-未超时
		第12位——报警确认位，1-确认、0-未确认；
	*/
	if (mode == TURN_HOLD)													//< 当前进行操作为：切换状态为保持状态
		Status[0] = (unsigned short)((Status[0] & 0xFF3F) | 0x40);			//< 清强制标志位，置保持标志位
	else if (mode == TURN_FORCE)											//< 当前进行操作为：切换状态为强制状态
		Status[0] = (unsigned short)((Status[0] & 0xFF3F) | 0x80);			//< 清保持标志位，置强制标志位
	else if (mode == TURN_TIMEOUT)											//< 当前进行操作为：切换状态为超时状态
		Status[0] = (unsigned short)(Status[0] | 0x100);					//< 置IO超时标志位为1
	else if (mode == WRITE)													//< 当前进行操作为：写入采样值
		Status[0] = (unsigned short)(Status[0] & 0xEFF);					//< 当执行写入采样值时，说明卡件有新值进入，不存在超时情况，则清IO超时标志位
	else if (mode == TURN_NORMAL)											//< 当前进行操作为：切换状态为非强制和非保持状态
		Status[0] = (unsigned short)(Status[0] & 0xFF3F);					//< 清强制和保持标志位
	else if (mode == ALARM_SURE)											//< 当前进行操作为：进行报警确认操作
		Status[0] = (unsigned short)(Status[0] | 0x800);					//< 置报警确认位为1

	//< 有采样值或强制值写入时，进行报警判断
	if (mode == WRITE || mode == TURN_FORCE)
		AV_Alarm(base);
	/*******************************************************/
	/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
	/*******************************************************/

	/*
		判断状态是否发生改变，改变是要置广播标志为1
			在此处进行判断的原因：每一次对数据点进行写入操作，数据点的前一时刻状态都会被刷新；
			当广播周期比较大时，很有可能随着数据点状态被刷新两次，前后状态由先前的不等于状态就变为了等于状态
			此时本该被广播的状态就遗漏了，所以要在此处对标志进行置位操作。
	*/
	if (Status[0] != PreStatus[0])
		BcTimeFlg_Set(&base->Base_Property, BCIO_AND_TIME);

#undef max
#undef min
#undef sum_calu
#undef bubble_sort
}

//************************************
// Method:    	BCAVIO						广播模拟量通道
// Programmer:  lww
// Time:		2019/2/22
// Returns:   	int
// Parameter: 	AVChannel_t * base			模拟量通道属性结构体指针
// Parameter: 	void * * Data				打包数据指针的指针
// Parameter: 	short * BCChannelNum		打包数据点数量计数值的指针
//************************************
int BCAVIO(AVChannel_t* base, void** Data, short* BCChannelNum)
{
#define MEMWRITE1(name)	MEMWRITE(name, Data[0])

	int _iLen = 0;
	bool* Is_BcFlg;				//< 是否广播当前点标志
	struct timespec ts;
	struct timespec* LastReportTime;
	float* CurOut_Value;
	float* LastBC_Value;
	float* ExcDead;
	int* ExcMaxTime;
	int* ExcMinTime;
	unsigned short* Status;
	int dt_ms;						//< 当前时间与上一次广播时间的时间差

	if (base->Pra_Property.IsUse_Flg)
	{
		/*******************************************************/
		/*<<<<<<<<<<<<<<<<<<<<   赋值操作   >>>>>>>>>>>>>>>>>>>*/
		/*******************************************************/
		LastReportTime = &(base->Base_Property.LastReportTime);
		CurOut_Value = &(base->Sta_Property.CurOut_Value);
		LastBC_Value = &(base->Sta_Property.LastBC_Value);
		ExcDead = &(base->Pra_Property.ExcDead);
		ExcMaxTime = &(base->Pra_Property.ExcMaxTime);
		ExcMinTime = &(base->Pra_Property.ExcMinTime);
		Status = &(base->Base_Property.Status);
		Is_BcFlg = &(base->Base_Property.Is_BcFlg);
		/*******************************************************/
		/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
		/*******************************************************/

		/*******************************************************/
		/*<<<<<<<<<<<<<<<<<<<<   打包操作   >>>>>>>>>>>>>>>>>>>*/
		/*******************************************************/
		//ts = RealTime_Get();																			//< 获取当前时间
		//dt_ms = Dt_Calu(ts, LastReportTime[0]);															//< 计算当前时间与上一次广播时间的时间差
		//if ((dt_ms >= ExcMinTime[0]) && (fabsf(CurOut_Value[0] - LastBC_Value[0]) > ExcDead[0]))		//< 大于例外报告最小时间间隔且大于例外报告死区
		//	BcTimeFlg_Set(&base->Base_Property, BCIO);
		//else if (dt_ms >= ExcMaxTime[0])																//< 大于例外报告最大时间间隔
		//	BcTimeFlg_Set(&base->Base_Property, BCIO);
		//else if ((Status[0] & 0x20) != 0)																//< 当前为报警状态
		//	BcTimeFlg_Set(&base->Base_Property, BCIO);

		//上位的最小广播时间暂时由500改为100
		ts = RealTime_Get();																			//< 获取当前时间
		dt_ms = Dt_Calu(ts, LastReportTime[0]);															//< 计算当前时间与上一次广播时间的时间差
		//if ((dt_ms >= 100) && (fabsf(CurOut_Value[0] - LastBC_Value[0]) >= ExcDead[0]))		//< 大于例外报告最小时间间隔且大于例外报告死区
		if ((dt_ms >= 100) && (fabsf(CurOut_Value[0] - LastBC_Value[0]) >= 0.01))		//< 大于例外报告最小时间间隔且大于例外报告死区
			BcTimeFlg_Set(&base->Base_Property, BCIO);
		else if (dt_ms >= ExcMaxTime[0])																//< 大于例外报告最大时间间隔
			BcTimeFlg_Set(&base->Base_Property, BCIO);
		else if ((Status[0] & 0x20) != 0)																//< 当前为报警状态
			BcTimeFlg_Set(&base->Base_Property, BCIO);


		/*if (base->Base_Property.ID == 8454592)
			printf("the SINE-AO value is %f\r\n", base->Sta_Property.CurOut_Value);*/

		if (Is_BcFlg[0])							
		{
			MEMWRITE1(base->Base_Property.ID);
			MEMWRITE1(base->Base_Property.Status);
			MEMWRITE1(base->Sta_Property.CurOut_Value);
			MEMWRITE1(base->Base_Property.Is_BcTimeFlg);
			if (base->Base_Property.Is_BcTimeFlg)
				MEMWRITE1(base->Base_Property.AlarmTime);

			LastBC_Value[0] = CurOut_Value[0];					//< duan0604 添加
			LastReportTime[0] = ts;
			BCChannelNum[0]++;

			BcTimeFlg_Set(&base->Base_Property, NOT_BCIO);
			
			//< duan20200423测试
		/*	if (base->Base_Property.ID == 50331840)
				printf("the SINE-AO value is %f\r\n", base->Sta_Property.CurOut_Value);
			if (base->Base_Property.ID == 50331841)
				printf("the SINE-AO value is %f\r\n", base->Sta_Property.CurOut_Value);*/
			/*if (base->Base_Property.ID == 50331718)
				printf("the SINE-AO value is %f\r\n", base->Sta_Property.CurOut_Value);*/
		}
		/*******************************************************/
		/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
		/*******************************************************/
	}
#undef MEMWRITE1

	return _iLen;
}

//************************************
// Method:    	BCDVIO						广播数字量通道
// Programmer:  lww
// Time:		2019/2/22
// Returns:   	int							打包长度
// Parameter: 	DVChannel_t * base			数字量通道属性结构体指针
// Parameter: 	void * * Data				打包数据指针的指针
// Parameter: 	short * BCChannelNum		打包数据点数量计数值的指针
//************************************
int BCDVIO(DVChannel_t* base, void** Data, short* BCChannelNum)
{
#define MEMWRITE1(name)	MEMWRITE(name, Data[0])

	int _iLen = 0;
	bool* Is_BcFlg;				//< 是否广播当前点标志
	struct timespec ts;
	struct timespec* LastReportTime;
	bool* CurOut_Value;
	bool* LastBC_Value;
	int* ExcMaxTime;
	int* ExcMinTime;
	unsigned short* Status;
	int dt_ms;						//< 当前时间与上一次广播时间的时间差

	if (base->Pra_Property.IsUse_Flg)
	{
		/*******************************************************/
		/*<<<<<<<<<<<<<<<<<<<<   赋值操作   >>>>>>>>>>>>>>>>>>>*/
		/*******************************************************/
		LastReportTime = &(base->Base_Property.LastReportTime);
		CurOut_Value = &(base->Sta_Property.CurOut_Value);
		LastBC_Value = &(base->Sta_Property.LastBC_Value);
		ExcMaxTime = &(base->Pra_Property.ExcMaxTime);
		ExcMinTime = &(base->Pra_Property.ExcMinTime);
		Status = &(base->Base_Property.Status);
		Is_BcFlg = &(base->Base_Property.Is_BcFlg);
		/*******************************************************/
		/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
		/*******************************************************/

		/*******************************************************/
		/*<<<<<<<<<<<<<<<<<<<<   打包操作   >>>>>>>>>>>>>>>>>>>*/
		/*******************************************************/
		ts = RealTime_Get();																			//< 获取当前时间
		dt_ms = Dt_Calu(ts, LastReportTime[0]);			// 											//< 计算当前时间与上一次广播时间的时间差
		if ((dt_ms >= ExcMinTime[0]) && (CurOut_Value[0] != LastBC_Value[0]))						//< 大于例外报告最小时间间隔且大于例外报告死区
			BcTimeFlg_Set(&base->Base_Property, BCIO);
		else if (dt_ms >= ExcMaxTime[0])															//< 大于例外报告最大时间间隔
			BcTimeFlg_Set(&base->Base_Property, BCIO);
		else if ((Status[0] & 0x20) != 0)															//< 当前为报警状态
			BcTimeFlg_Set(&base->Base_Property, BCIO);

		if (Is_BcFlg[0])
		{
			MEMWRITE1(base->Base_Property.ID);
			MEMWRITE1(base->Base_Property.Status);
			MEMWRITE1(base->Sta_Property.CurOut_Value);
			MEMWRITE1(base->Base_Property.Is_BcTimeFlg);
			if (base->Base_Property.Is_BcTimeFlg)
				MEMWRITE1(base->Base_Property.AlarmTime);

			LastBC_Value[0] = CurOut_Value[0];					//< duan0608添加
			LastReportTime[0] = ts;
			BCChannelNum[0]++;

			BcTimeFlg_Set(&base->Base_Property, NOT_BCIO);
		}
		/*******************************************************/
		/*<<<<<<<<<<<<<<<<<<<<<<<操作结束>>>>>>>>>>>>>>>>>>>>>>*/
		/*******************************************************/
	}
#undef MEMWRITE1

	return _iLen;
}

//************************************
// Method:    	ChangeChannelPar			改变通道参数
// Programmer:  lww
// Time:		2019/4/3
// Returns:   	int
// Parameter: 	void * Channel_pt			通道指针
// Parameter: 	void * * Data				数据包指针的指针
// Parameter: 	Channel_type_e type			通道类型
//************************************
int ChangeChannelPar(void* Channel_pt, void** Data, Channel_type_e type)
{
	int _iLen = 0;
	if (type == AO_CHA || type == AI_CHA)
	{
		MEMREAD((((AVChannel_t*)(Channel_pt))->Pra_Property), Data[0]);
	}
	else if (type == DO_CHA || type == DI_CHA)
	{
		MEMREAD((((DVChannel_t*)(Channel_pt))->Pra_Property), Data[0]);
	}
	return _iLen;
}

//************************************
// Method:    	BakIO						备份IO硬点
// Programmer:  lww
// Time:		2019/3/6
// Returns:   	int							打包长度					
// Parameter: 	void * base					通道属性结构退指针
// Parameter: 	void * * Data				打包数据指针的指针
// Parameter: 	Channel_type_e type			通道类型
// Parameter: 	short * BakChannelNum		打包通道数计数值指针
//************************************
int BakIO(void* base, void** Data, Channel_type_e type, short* BakChannelNum)
{
#define MEMWRITE1(name)	MEMWRITE(name, Data[0])
	int _iLen = 0;
	bool IsUse_Flg;
	IsUse_Flg = (type == AI_CHA || type == AO_CHA) ? (((AVChannel_t*)base)->Pra_Property.IsUse_Flg) : (((DVChannel_t*)base)->Pra_Property.IsUse_Flg);
	if (IsUse_Flg)
	{
		//< 备份公有参数
		MEMWRITE1(((DVAV_BASE_Property_t*)base)->ID);
		MEMWRITE1(((DVAV_BASE_Property_t*)base)->LastReportTime);
		MEMWRITE1(((DVAV_BASE_Property_t*)base)->Status);
		MEMWRITE1(((DVAV_BASE_Property_t*)base)->PreStatus);
		MEMWRITE1(((DVAV_BASE_Property_t*)base)->AlarmTime);

		//< 备份私有参数
		if (type == AI_CHA || type == AO_CHA)
		{
			MEMWRITE1(((AVChannel_t*)base)->Sta_Property);
		}
		else
		{
			MEMWRITE1(((DVChannel_t*)base)->Sta_Property);
		}
		BakChannelNum[0]++;
	}
	return _iLen;
#undef MEMWRITE1
}

//************************************
// Method:    	Read_DATA_File		读取DATA文件
// Programmer:  lww
// Time:		2019/2/16
// Returns:   	bool
//************************************
bool Read_DATA_File()
{
#define WRONG_DEAL	{\
	printf("Read DATA file failed, please check problem!\r\n");\
	free(Data_Start);\
	return false;\
}
#define MEMREAD1(name)	MEMREAD(name, Data)
#define ADDCARD {\
	memset(&IO_PinNum, 0, sizeof(IO_Num_t));\
	MEMREAD1(IO_PinNum);\
	if ((AddCard(&Card_List[Card_Type], Card_ID, &IO_PinNum)) != LIST_OK) { WRONG_DEAL; }	/*动态生成模块*/\
	_base = (Card_Base_t*)SearchId(((List_Base_t*)Card_List[Card_Type].ppData[0]), Card_ID);\
	if (_base != NULL)\
	{\
		if (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->ChangeListPar != NULL)\
			_iLen += (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->ChangeListPar(_base, &Data));\
		if (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->ChangePar != NULL)\
		{\
			_base->Card_File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*duan20200107 卡件可调参数在数据库下装文件中的指针偏移量*/\
			_iLen += (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->ChangePar(_base, &Data));\
		}\	
		if (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->InitStaPar != NULL)\
			_iLen += (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->InitStaPar(_base, &Data));\
		for (j = 0; j < _base->IO_Num.AI_Num; j++)\
		{\
			MEMREAD1(_base->AIChannel[j].Base_Property.ID);\
			_base->AIChannel[j].Base_Property.File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*记录数据点参数在DATA文件中的指针偏移*/\
			_iLen += ChangeChannelPar(((void*)(&_base->AIChannel[j])), &Data, AI_CHA);\
			AV_Init(&(_base->AIChannel[j]));\
		}\
		for (j = 0; j < _base->IO_Num.AO_Num; j++)\
		{\
			MEMREAD1(_base->AOChannel[j].Base_Property.ID);\
			_base->AOChannel[j].Base_Property.File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*记录数据点参数在DATA文件中的指针偏移*/\
			_iLen += ChangeChannelPar(((void*)(&_base->AOChannel[j])), &Data, AO_CHA);\
			AV_Init(&(_base->AOChannel[j]));\
		}\
		for (j = 0; j < _base->IO_Num.DI_Num; j++)\
		{\
			MEMREAD1(_base->DIChannel[j].Base_Property.ID);\
			_base->DIChannel[j].Base_Property.File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*记录数据点参数在DATA文件中的指针偏移*/\
			_iLen += ChangeChannelPar(((void*)(&_base->DIChannel[j])), &Data, DI_CHA);\
			DV_Init(&(_base->DIChannel[j]));\
		}\
		for (j = 0; j < _base->IO_Num.DO_Num; j++)\
		{\
			MEMREAD1(_base->DOChannel[j].Base_Property.ID);\
			_base->DOChannel[j].Base_Property.File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*记录数据点参数在DATA文件中的指针偏移*/\
			_iLen += ChangeChannelPar(((void*)(&_base->DOChannel[j])), &Data, DO_CHA);\
			DV_Init(&(_base->DOChannel[j]));\
		}\
	}\
	else\
		WRONG_DEAL;\
}

	void* Data_Start = NULL;	//< 存放文件内容的内存的首地址
	void* Data = NULL;			//< 当前读写文件位置在内存中的地址
	int _iLen = 0;				//< 已经读取的字节个数
	int File_Size = 0;			//< 文件字节数
	int i, j;
	int Card_Type;
	unsigned int Card_ID;
	IO_Num_t IO_PinNum;
	Card_Base_t* _base;

	printf("Begin to read DATA file!\r\n");

	//timespec time1;		
	//timespec time2;		
	//time2 = RealTime_Get();

	//< 打开文件并读取文件到内存中
	if (Open2Read(DATA_FILE_PATH, &Data_Start, &Data, &File_Size) == false)
		return false;

	/*time1 = RealTime_Get();
	int aaa = Dt_Calu(time1, time2);
	printf("Send all time is %d ms!!!\n", aaa);*/

	/*******************解析文件信息**********************/

	/*读取站基准周期（ms）*/
	MEMREAD1(SYS_BaseTime);

	/*读取软点信息*/
	Card_ID = SAMAIO_CARD_ID;//< 软点卡件的ID号默认为0，硬点卡件的ID号从1开始编号
	Card_Type = TYP_SAMAIO;
	ADDCARD;

	/*读取硬点卡件的信息*/
 	MEMREAD1(Card_TotalNum);//< 读取硬点卡件数量

	for (i = 0; i < Card_TotalNum; i++)
	{
		MEMREAD1(Card_ID);
		MEMREAD1(Card_Type);
		//< ADDCARD;
		memset(&IO_PinNum, 0, sizeof(IO_Num_t));
		MEMREAD1(IO_PinNum);
		if ((AddCard(&Card_List[Card_Type], Card_ID, &IO_PinNum)) != LIST_OK) { WRONG_DEAL; }	/*动态生成模块*/
		_base = (Card_Base_t*)SearchId(((List_Base_t*)Card_List[Card_Type].ppData[0]), Card_ID);
		if (_base != NULL)
		{
			if (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->ChangeListPar != NULL)
				_iLen += (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->ChangeListPar(_base, &Data));
			if (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->ChangePar != NULL)
			{
				_base->Card_File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*duan20200107 卡件可调参数在数据库下装文件中的指针偏移量*/
				_iLen += (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->ChangePar(_base, &Data));
			}

			if (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->InitStaPar != NULL)
				_iLen += (((Card_List_Func_t*)(Card_List[Card_Type].List_Func))->InitStaPar(_base, &Data));
			for (j = 0; j < _base->IO_Num.AI_Num; j++)
			{
				MEMREAD1(_base->AIChannel[j].Base_Property.ID);/*此处ID指的是索引号*/
				_base->AIChannel[j].Base_Property.File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*记录数据点参数在DATA文件中的指针偏移*/
				_iLen += ChangeChannelPar(((void*)(&_base->AIChannel[j])), &Data, AI_CHA);/*读取AI卡件可调参数*/
				AV_Init(&(_base->AIChannel[j]));
			}
			for (j = 0; j < _base->IO_Num.AO_Num; j++)
			{
				MEMREAD1(_base->AOChannel[j].Base_Property.ID);
				_base->AOChannel[j].Base_Property.File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*记录数据点参数在DATA文件中的指针偏移*/
				_iLen += ChangeChannelPar(((void*)(&_base->AOChannel[j])), &Data, AO_CHA);
				AV_Init(&(_base->AOChannel[j]));
			}
			for (j = 0; j < _base->IO_Num.DI_Num; j++)
			{
				MEMREAD1(_base->DIChannel[j].Base_Property.ID);
				_base->DIChannel[j].Base_Property.File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*记录数据点参数在DATA文件中的指针偏移*/
				_iLen += ChangeChannelPar(((void*)(&_base->DIChannel[j])), &Data, DI_CHA);
				DV_Init(&(_base->DIChannel[j]));
			}
			for (j = 0; j < _base->IO_Num.DO_Num; j++)
			{
				MEMREAD1(_base->DOChannel[j].Base_Property.ID);
				_base->DOChannel[j].Base_Property.File_Read_Offset = (long)((char*)Data - (char*)Data_Start);/*记录数据点参数在DATA文件中的指针偏移*/
				_iLen += ChangeChannelPar(((void*)(&_base->DOChannel[j])), &Data, DO_CHA);
				DV_Init(&(_base->DOChannel[j]));
			}
		}
		else
			WRONG_DEAL;

	}

	//< 长度校验
	if (File_Size == _iLen)
	{
		free(Data_Start);	//< 释放内存
	}
		
	else
		WRONG_DEAL;

#undef MEMREAD1
#undef WRONG_DEAL
#undef ADDCARD

	printf("Read DATA file success!\r\n");
	return true;
}

//************************************
// Method:    	tReadCard					读卡线程
// Programmer:  lww
// Time:		2019/2/16
// Returns:   	void *
// Parameter: 	void * arg
//************************************
void *tReadCard(void* arg)
{
	printf("the Kernel_%s is %d\r\n", "tReadCard_ID", syscall(SYS_gettid));			//< duan20200610

	//< 由于串口通讯设备只有一套（两个，但互为冗余，所以共一套），因此MODBUS-485主站通讯卡与从站通讯卡不可同时存在
	if ((Card_List[TYPE(MODBUS485_MASTER)].iCount > 0) && (Card_List[TYPE(MODBUS485_SLAVE)].iCount > 0))
		printf("Modbus-485 communication don't allowed be both master and slave!!! please check and down DATA file again!\r\n");
	//< MODBUS485_MASTER通讯卡存在时
	else if ((Card_List[TYPE(MODBUS485_MASTER)].iCount) > 0)
		CARD_WORK(MODBUS485_MASTER)();
	//< MODBUS485_SLAVE通讯卡存在时
	else if ((Card_List[TYPE(MODBUS485_SLAVE)].iCount) > 0)
		CARD_WORK(MODBUS485_SLAVE)();

	//< 由于网口通讯设备只有一套（两个，但互为冗余，所以共一套），因此MODBUS-TCP主站通讯卡与从站通讯卡不可同时存在
	if ((Card_List[TYPE(MODBUSTCP_MASTER)].iCount > 0) && (Card_List[TYPE(MODBUSTCP_SLAVE)].iCount > 0))
		printf("Modbus-TCP communication don't allowed be both master and slave!!! please check and down DATA file again!\r\n");
	//< MODBUSTCP_MASTER通讯卡存在时
	else if ((Card_List[TYPE(MODBUSTCP_MASTER)].iCount) > 0)
		CARD_WORK(MODBUSTCP_MASTER)();
	//< MODBUSTCP_SLAVE通讯卡存在时
	else if ((Card_List[TYPE(MODBUSTCP_SLAVE)].iCount) > 0)
		CARD_WORK(MODBUSTCP_SLAVE)();

	//< 由于网口通讯设备只有一套（两个，但互为冗余，所以共一套），因此IEC104-TCP主站通讯卡与从站通讯卡不可同时存在
	/*if ((Card_List[TYPE(IEC104TCP_MASTER)].iCount > 0) && (Card_List[TYPE(IEC104TCP_SLAVE)].iCount > 0))
		printf("IEC104-TCP communication don't allowed be both master and slave!!! please check and down DATA file again!\r\n");*/
	//< IEC104TCP_MASTER通讯卡存在时
    if ((Card_List[TYPE(IEC104TCP_MASTER)].iCount) > 0)
		CARD_WORK(IEC104TCP_MASTER)();
	//< IEC104TCP_SLAVE通讯卡存在时
    if ((Card_List[TYPE(IEC104TCP_SLAVE)].iCount) > 0)
		CARD_WORK(IEC104TCP_SLAVE)();
	// 功率预测卡件（读文件，通过ftp协议传过来的）///功能尚未实现，因要求更改已经弃用
	if ((Card_List[TYPE(LOAD_FORECAST)].iCount) > 0)
		CARD_WORK(LOAD_FORECAST)();


	SYS_Calcu_Start = true;

	return NULL;
}


//************************************
// Method:    	SamaIOSetLink			软点建立映射关系
// Programmer:  lww
// Time:		2019/4/21
// Returns:   	List_Info_e
//************************************
List_Info_e SamaIOSetLink()
{
#define FREE {\
	myfree_Pt((void**)& SAMAIO_AO_Map);\
	myfree_Pt((void**)& SAMAIO_DO_Map);\
	myfree_Pt((void**)& SAMAIO_AO_PinSta_Map);\
	myfree_Pt((void**)& SAMAIO_DO_PinSta_Map);\
}

	Card_Base_t* _base;
	int i;
	unsigned int Model_ID = 0;			//< 模块ID号
	unsigned int Pt_ptr = 0;			//< 引脚号

	FREE;

	_base = (Card_Base_t*)SearchId(((List_Base_t*)Card_List[TYP_SAMAIO].ppData[0]), SAMAIO_CARD_ID);
	if (_base != NULL)
	{
		if (_base->IO_Num.AO_Num > 0)
		{
			if ((SAMAIO_AO_Map = (float**)calloc(_base->IO_Num.AO_Num, sizeof(float*))) == NULL || (SAMAIO_AO_PinSta_Map = (IOSta_e**)calloc(_base->IO_Num.AO_Num, sizeof(IOSta_e*))) == NULL)
			{ 
				FREE; 
				return LIST_MEMFAIL; 
			}
			for (i = 0; i < _base->IO_Num.AO_Num; i++)
			{
				SamaIOGetModel(_base, i, &Model_ID, &Pt_ptr,AO_CHA);
				if (SamaIO_Find(Model_ID, Pt_ptr, AO_CHA, &(SAMAIO_AO_PinSta_Map[i]), (void**)&(SAMAIO_AO_Map[i])) == false)
					printf("Do not find  the mapping relation between SAMAIO and Model \r\n");
			}
		}
		if (_base->IO_Num.DO_Num > 0)
		{
			if ((SAMAIO_DO_Map = (bool**)calloc(_base->IO_Num.DO_Num, sizeof(bool*))) == NULL || (SAMAIO_DO_PinSta_Map = (IOSta_e**)calloc(_base->IO_Num.DO_Num, sizeof(IOSta_e*))) == NULL)
			{ 
				FREE; 
				return LIST_MEMFAIL; 
			}
			for (i = 0; i < _base->IO_Num.DO_Num; i++)
			{
				SamaIOGetModel(_base, i, &Model_ID, &Pt_ptr,DO_CHA);
				if (SamaIO_Find(Model_ID, Pt_ptr, DO_CHA, &(SAMAIO_DO_PinSta_Map[i]), (void**)&(SAMAIO_DO_Map[i])) == false)
					printf("Do not find  the mapping relation between SAMAIO and Model \r\n");
			}
		}
		SYS_SAMAIO_Start = true;		
		return LIST_OK;
	}
	return LIST_IDNON;

#undef MODELNUM_INPAGE_GET
#undef PTPTR_GET
#undef PAGE_GET
#undef FREE
}


//************************************
// Method:    	SamaIOGetModel			    根据软点通道信息，获得对应SAMA模块ID、引脚号
// Programmer:  duan
// Time:		2019/5/23
// Returns:   	void										
// Parameter: 	Card_Base_t* base			卡件基本变量结构体指针
// Parameter: 	int i						记录卡件中通道位置
// Parameter: 	unsigned int* Model_ID		模块ID指针
// Parameter: 	unsigned int* Pt_ptr		模块引脚指针
// Parameter:   Channel_type_e type         通道类型（模拟、数字）
//************************************
void SamaIOGetModel(Card_Base_t* base, int i, unsigned int* Model_ID, unsigned int* Pt_ptr,Channel_type_e type)
{
#define MODELNUM_INPAGE_GET(name)	((name & 0x7FC0) >> 6)
#define PTPTR_GET(name)		(name & 0x3F)
#define PAGE_GET(name)		((name & 0xFF8000) >> 15)

	unsigned int Page_Num;			//< 页号
	unsigned int Model_NumInPage;	//< 模块在当前页的序号
	if (type == AO_CHA)
	{
		Page_Num = PAGE_GET(base->AOChannel[i].Base_Property.ID);
		Model_NumInPage = MODELNUM_INPAGE_GET(base->AOChannel[i].Base_Property.ID);
		Model_ID[0] = ((Page_Num << 9) | Model_NumInPage);
		Pt_ptr[0] = PTPTR_GET(base->AOChannel[i].Base_Property.ID);
	}
	else
	{
		Page_Num = PAGE_GET(base->DOChannel[i].Base_Property.ID);
		Model_NumInPage = MODELNUM_INPAGE_GET(base->DOChannel[i].Base_Property.ID);
		Model_ID[0] = ((Page_Num << 9) | Model_NumInPage);
		Pt_ptr[0] = PTPTR_GET(base->DOChannel[i].Base_Property.ID);
	}	

#undef  MODELNUM_INPAGE_GET
#undef  PTPTR_GET
#undef	PAGE_GET
}

//************************************
// Method:    	IP_Get						IP地址获取
// Programmer:  lww
// Time:		2019/7/14
// Returns:   	void
// Parameter: 	char * IP					存放IP地址的数组指针
// Parameter: 	unsigned char IP_Addr		末尾IP地址
// Parameter: 	Net_Chosen_e Net_Chosen		选取的网段
//************************************
void IP_Get(char* IP, unsigned char IP_Addr, Net_Chosen_e Net_Chosen)
{
#define IP_NUM_LENGTH	4
	char IP_Str[IP_NUM_LENGTH] = { 0 };

	if (Net_Chosen == NETA)
		strcpy(IP, NETA_NAME);
	if (Net_Chosen == NETB)
		strcpy(IP, NETB_NAME);

	Int2String(IP_Addr, IP_Str, 10);
	strcat(IP, IP_Str);
#undef IP_NUM_LENGTH
}